home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / general / modelers / geomview / source.lha / Geomview / src / lib / camera / camera.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-01  |  15.1 KB  |  629 lines

  1. /* Copyright (c) 1992 The Geometry Center; University of Minnesota
  2.    1300 South Second Street;  Minneapolis, MN  55454, USA;
  3.    
  4. This file is part of geomview/OOGL. geomview/OOGL is free software;
  5. you can redistribute it and/or modify it only under the terms given in
  6. the file COPYING, which you should have received along with this file.
  7. This and other related software may be obtained via anonymous ftp from
  8. geom.umn.edu; email: software@geom.umn.edu. */
  9. static char *copyright = "Copyright (C) 1992 The Geometry Center";
  10.  
  11. /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
  12.  
  13. #include <math.h>
  14. #include "geomclass.h"
  15. #include "cameraP.h"
  16. #include "mg.h"
  17.  
  18. Camera * _CamSet(Camera *cam, int attr, va_list *a_list);
  19.  
  20. #define SETFLAG(flag, bit, value)    \
  21.     if (value) flag |= bit;        \
  22.     else     flag &= ~bit
  23.  
  24. #define GETFLAG(flag, bit)    ( (flag & bit) != 0 )
  25.  
  26. static float GetHalfField( register Camera *cam );
  27. static void SetHalfField( register Camera *cam, float halffield );
  28.  
  29. Camera *
  30. CamCreate(int a1, ...)
  31. {
  32.   register Camera *thiscam;
  33.   va_list a_list;
  34.  
  35.   thiscam = OOGLNewE(Camera, "CamCreate: unable to allocate camera\n");
  36.   if (thiscam == NULL) return(NULL);
  37.  
  38.   RefInit((Ref *)thiscam, CAMMAGIC);
  39.  
  40.   CamDefault(thiscam);
  41.   thiscam->changed = 0;
  42.   
  43.   va_start( a_list, a1 );
  44.   _CamSet(thiscam, a1, &a_list);
  45.   va_end(a_list);
  46.   return thiscam;
  47. }
  48.  
  49. void
  50. CamDefault(Camera *cam)
  51. {
  52.   cam->flag = CAMF_PERSP;
  53.   cam->frameaspect = 4.0/3.0;
  54.   cam->focus = 3.0;
  55.   cam->stereo_sep = 0.5;
  56.   cam->stereo_angle = .08;
  57.   cam->c2whandle = NULL;
  58.   cam->w2chandle = NULL;
  59.   cam->sterhandle[0] = NULL;
  60.   cam->sterhandle[1] = NULL;
  61.   CamStereoCompute(cam);
  62.   cam->whicheye = 0;        /* only applies to stereo */
  63.   cam->space = TM_EUCLIDEAN;
  64.   CamReset( cam );
  65. }
  66.  
  67. Camera *
  68. CamSet(Camera *cam, int a1, ...)
  69. {
  70.   va_list a_list;
  71.   
  72.   va_start(a_list, a1);
  73.   return ( _CamSet(cam, a1, &a_list) );
  74. }
  75.  
  76. Camera *
  77. _CamSet(Camera *cam, int attr, register va_list *alist)
  78. {
  79.   Transform * tt;
  80.   int sethalffield = 0, setaspect = 0, setstereogeom = 0;
  81.   double halffield, v;
  82.   Handle *h;
  83.   int bit, unbit;
  84.   char **ablock = NULL;
  85.  
  86. #define NEXT(type) OOGL_VA_ARG(type,alist,ablock)
  87.  
  88.   while (attr != CAM_END) {
  89.     bit = unbit = 0;
  90.     switch(attr) {
  91.     case CAM_ABLOCK:
  92.         ablock = NEXT(char**);
  93.         break;
  94.     case CAM_C2W:
  95.     tt = NEXT(Transform *);
  96.     bit = CAMF_NEWC2W, unbit = CAMF_W2C;
  97.     TmCopy(tt, cam->camtoworld);
  98.     TmInvert( cam->camtoworld, cam->worldtocam);
  99.     break;
  100.     case CAM_W2C:
  101.     tt = NEXT(Transform *);
  102.     bit = CAMF_W2C, unbit = CAMF_NEWC2W;
  103.     TmCopy(tt, cam->worldtocam);
  104.     TmInvert(cam->worldtocam, cam->camtoworld);
  105.     break;
  106.     case  CAM_FOV:
  107.     v = NEXT(double) / 2;
  108.     bit = CAMF_FOV;
  109.     if(cam->flag & CAMF_PERSP) {
  110.         if(v >= 180/2) v = 120/2;
  111.         v = tan( RADIANS(v) );
  112.     }
  113.     halffield = v;
  114.     sethalffield = 1;
  115.     break;
  116.     case  CAM_HALFYFIELD:
  117.     cam->halfyfield = NEXT(double);
  118.     if(cam->flag & CAMF_PERSP)
  119.         cam->halfyfield *= cam->focus;
  120.     bit = CAMF_FOV;
  121.     break;
  122.     case  CAM_HALFFIELD:
  123.     halffield = NEXT(double);
  124.     sethalffield = 1;
  125.     bit = CAMF_FOV;
  126.     break;
  127.     case  CAM_ASPECT:
  128.     if((v = NEXT(double)) > 0.) {
  129.         if (!sethalffield)
  130.         halffield = GetHalfField(cam);
  131.         cam->frameaspect = v;
  132.         bit = CAMF_ASPECT;
  133.         setaspect = 1;
  134.     }
  135.     break;
  136.     case  CAM_FOCUS:
  137.     if((v = NEXT(double)) > 0) {
  138.         if(cam->flag & CAMF_PERSP)
  139.         cam->halfyfield *= v / cam->focus;
  140.         cam->focus = v;
  141.         bit = CAMF_FOCUS;
  142.     }
  143.     break;
  144.     case  CAM_NEAR:
  145.     cam->near = NEXT(double);
  146.     bit = CAMF_NEAR;
  147.     break;
  148.     case  CAM_FAR:
  149.     cam->far = NEXT(double);
  150.     bit = CAMF_FAR;
  151.     break;
  152.     case  CAM_STEREOSEP:
  153.     cam->stereo_sep = NEXT(double);
  154.     bit = CAMF_STEREOGEOM, unbit = CAMF_STEREOXFORM;
  155.     setstereogeom = 1;
  156.     break;
  157.     case  CAM_STEREOANGLE:
  158.     cam->stereo_angle = NEXT(double);
  159.     bit = CAMF_STEREOGEOM, unbit = CAMF_STEREOXFORM;
  160.     setstereogeom = 1;
  161.     break;
  162.     case  CAM_STEREOEYE:
  163.     cam->whicheye = NEXT(int);
  164.     bit = CAMF_EYE;
  165.     break;
  166.     case CAM_PERSPECTIVE:  bit = CAMF_PERSP; goto flagbit;
  167.     case CAM_STEREO:        bit = CAMF_STEREO; goto flagbit;
  168.      flagbit:
  169.     SETFLAG(cam->flag, bit, NEXT(int));
  170.     break;
  171.     case CAM_STEREYES:
  172.     bcopy(NEXT(float *), cam->stereyes, 2*sizeof(Transform));
  173.     bit = CAMF_STEREOXFORM, unbit = CAMF_STEREOGEOM;
  174.     break;
  175.     case CAM_STERHANDLES:
  176.     bcopy(NEXT(Handle **), cam->sterhandle, 2*sizeof(Handle *));
  177.     bit = CAMF_STEREOXFORM, unbit = CAMF_STEREOGEOM;
  178.     break;
  179.     case CAM_C2WHANDLE:
  180.     h = NEXT(Handle *);
  181.     if(cam->c2whandle && cam->c2whandle != h)
  182.         HandlePDelete(&cam->c2whandle);
  183.     cam->c2whandle = h;
  184.     HandleRegister(&cam->c2whandle, (Ref *)cam, cam->camtoworld, CamTransUpdate);
  185.     bit = CAMF_NEWC2W, unbit = CAMF_W2C;
  186.     break;
  187.  
  188.     case CAM_W2CHANDLE:
  189.     h = NEXT(Handle *);
  190.     HandlePDelete(&cam->w2chandle);
  191.     cam->w2chandle = h;
  192.     HandleRegister(&cam->w2chandle, (Ref *)cam, cam->worldtocam, CamTransUpdate);
  193.     bit = CAMF_W2C, unbit = CAMF_NEWC2W;
  194.     break;
  195.     case CAM_SPACE:
  196.     {
  197.       int space = NEXT(int);
  198.       if (   space != TM_EUCLIDEAN
  199.           && space != TM_HYPERBOLIC
  200.           && space != TM_SPHERICAL) {
  201.         OOGLError(0,"illegal space value %1d\n", space);
  202.       } else {
  203.         cam->space = space;
  204.         bit = CAMF_SPACE;
  205.       }
  206.     }
  207.     break;
  208.     default:
  209.     OOGLError (0, "CamSet: Undefined attribute: %d", attr);
  210.     return NULL;
  211.     }
  212.     cam->changed &= ~unbit;
  213.     cam->changed |= bit;
  214.     attr = NEXT(int);
  215.   }
  216.  
  217. /*
  218.   (sethalffield) means we have a new halffield value, stored in local
  219.   var "halffield". (setaspect) means we have a new aspect ratio, and the
  220.   halffield must be updated in accordance with this.  In this case,
  221.   "halffield" holds either the original halffield value, if a new one
  222.   hasn't been explicitly set with CAM_HALFFIELD or CAM_FOV, or the
  223.   new value, if it was explicitly set.  All of these cases are dealt with
  224.   by the following call to SetHalfField.
  225. */
  226.   if (setaspect || sethalffield)
  227.     SetHalfField(cam, halffield);
  228.  
  229.   /* following works since the only way to change stereo parameters is
  230.      by using this routine */
  231.   if (setstereogeom)
  232.     CamStereoCompute(cam);
  233.   
  234.   return cam;
  235.  
  236. #undef NEXT
  237.  
  238. }
  239.  
  240.  
  241. /*-----------------------------------------------------------------------
  242.  * Function:    CamGet
  243.  * Description:    query a camera
  244.  * Args:    *cam: the camera to query
  245.  *        attr: the attribute to query
  246.  *        value: attr's value is written here
  247.  * Returns:    1:  attr is valid and value has been written
  248.  *        0:  attr is valid but currently does not
  249.  *            have a value
  250.  *        -1: invalid attr
  251.  * Author:    mbp
  252.  * Date:    Thu Aug  8 10:09:10 1991
  253.  * Notes:    At present, there are no camera attr's that might
  254.  *        not be set, so 0 is never returned.  This might change
  255.  *        in the future.
  256.  */
  257. int
  258. CamGet(register Camera *cam, int attr, void *value)
  259. {
  260. #define VALUE(type) ((type*)value)
  261.  
  262.   switch (attr) {
  263.  
  264.   case CAM_PERSPECTIVE:
  265.     *VALUE(int) = GETFLAG(cam->flag, CAMF_PERSP);
  266.     break;
  267.  
  268.   case CAM_STEREO:
  269.     *VALUE(int) = GETFLAG(cam->flag, CAMF_STEREO);
  270.     break;
  271.  
  272.   case CAM_C2W:
  273.     /* camtoworld is always up to date, so just copy */
  274.     TmCopy( cam->camtoworld, VALUE(Transform) );
  275.     break;
  276.  
  277.   case CAM_W2C:
  278.     /* worldtocam is not always up to date, so update if necessary ... */
  279.     if (cam->flag & CAMF_NEWC2W ) {
  280.     TmInvert( cam->camtoworld, cam->worldtocam );
  281.     cam->flag &= ~CAMF_NEWC2W;
  282.     }
  283.     /* ... then copy */
  284.     TmCopy( cam->worldtocam, VALUE(Transform) );
  285.     break;
  286.  
  287.   case CAM_FOV:
  288.     *VALUE(float) = 2 * ( (cam->flag & CAMF_PERSP)
  289.               ? DEGREES( atan( (double)(GetHalfField(cam)) ) )
  290.               : GetHalfField(cam));
  291.     break;
  292.  
  293.   case CAM_HALFYFIELD:
  294.     *VALUE(float) = (cam->flag & CAMF_PERSP) ? cam->halfyfield / cam->focus
  295.                          : cam->halfyfield;
  296.     break;
  297.  
  298.   case CAM_HALFFIELD:
  299.     *VALUE(float) = GetHalfField(cam);
  300.     break;
  301.  
  302.   case CAM_ASPECT:
  303.     *VALUE(float) = cam->frameaspect;
  304.     break;
  305.  
  306.   case CAM_FOCUS:
  307.     *VALUE(float) = cam->focus;
  308.     break;
  309.  
  310.   case CAM_NEAR:
  311.     *VALUE(float) = cam->near;
  312.     break;
  313.  
  314.   case CAM_FAR:
  315.     *VALUE(float) = cam->far;
  316.     break;
  317.  
  318.   case CAM_STEREOSEP:
  319.     *VALUE(float) = cam->stereo_sep;
  320.     break;
  321.  
  322.   case CAM_STEREOANGLE:
  323.     *VALUE(float) = cam->stereo_angle;
  324.     break;
  325.  
  326.   case CAM_STEREOEYE:
  327.     *VALUE(int) = cam->whicheye;
  328.     break;
  329.  
  330.   case CAM_C2WHANDLE:
  331.     *VALUE(Handle *) = cam->c2whandle;
  332.     break;
  333.  
  334.   case CAM_W2CHANDLE:
  335.     *VALUE(Handle *) = cam->w2chandle;
  336.     break;
  337.  
  338.   case CAM_STEREYES:
  339.     bcopy(cam->stereyes, value, 2*sizeof(Transform));
  340.     break;
  341.  
  342.   case CAM_STERHANDLES:
  343.     bcopy(cam->sterhandle, value, 2*sizeof(Handle *));
  344.     break;
  345.  
  346.   case CAM_SPACE:
  347.     *VALUE(int) = cam->space;
  348.     break;
  349.  
  350.   default:
  351.     return -1;
  352.     break;
  353.   }
  354.   return 1;
  355.  
  356. #undef VALUE
  357. }
  358.  
  359. void
  360. CamDelete( register Camera *cam )
  361. {
  362.     if(cam && RefDecr((Ref *)cam) <= 0) {
  363.     cam->magic = -1;    /* Invalidate */
  364.     if(cam->c2whandle) HandlePDelete( &cam->c2whandle );
  365.     if(cam->w2chandle) HandlePDelete( &cam->w2chandle );
  366.     if(cam->sterhandle[0]) HandlePDelete( &cam->sterhandle[0] );
  367.     if(cam->sterhandle[1]) HandlePDelete( &cam->sterhandle[1] );
  368.     OOGLFree(cam);
  369.     }
  370. }
  371.  
  372. Camera *
  373. CamCopy( Camera *src, register Camera *dst )
  374. {
  375.     if(src == NULL)
  376.     return NULL;
  377.     if(dst == NULL)
  378.     dst = OOGLNewE(Camera, "CamCopy Camera");
  379.     else
  380.     HandleDelete(dst->handle);
  381.     *dst = *src;
  382.     dst->ref_count = 1;
  383.     dst->handle = NULL;
  384.     return dst;
  385. }
  386.  
  387. void
  388. CamReset( register Camera *cam )
  389. {
  390.   Transform T;
  391.   int persp;
  392.  
  393.   CamGet(cam, CAM_PERSPECTIVE, &persp);
  394.  
  395.   switch (cam->space) {
  396.  
  397.   case TM_EUCLIDEAN:
  398.     CamSet( cam,
  399.        CAM_NEAR,        .07,
  400.        CAM_FAR,        100.0,
  401.        CAM_FOCUS,        3.0,
  402.        CAM_FOV,        persp ? 40.0 : 2.2,
  403.        CAM_END);
  404.     break;
  405.  
  406.   case TM_HYPERBOLIC:
  407.     CamSet( cam,
  408.        CAM_NEAR,        .07,
  409.        CAM_FAR,        100.0,
  410.        CAM_FOCUS,        2.5, 
  411.        CAM_FOV,        persp ? 40.0 : 2.2,
  412.        CAM_END);
  413.     break;
  414.  
  415.   case TM_SPHERICAL:
  416.     CamSet( cam,
  417.        CAM_NEAR,        .05,
  418.        CAM_FAR,        -.05,
  419.        CAM_FOCUS,        0.5,
  420.        CAM_FOV,        persp ? 90.0 : 2.2,
  421.        CAM_END);
  422.     break;
  423.   }
  424.  
  425.   TmSpaceTranslate( T, 0.0, 0.0, cam->focus, cam->space );
  426.   CamSet(cam, CAM_C2W, T, CAM_END);
  427. }
  428.  
  429. /*
  430.  * Return camera's projection transform in proj.
  431.  * See CamView below for the range of the projection.
  432.  */
  433. void
  434. CamViewProjection( register Camera *cam, register Transform proj )
  435. {
  436.     float y;
  437.     float x;
  438.  
  439.     y = cam->halfyfield;
  440.     if(cam->flag & CAMF_PERSP)
  441.     y *= cam->near / cam->focus;
  442.     x = cam->frameaspect * y;
  443.  
  444.     if(cam->flag & CAMF_PERSP) {
  445.     TmPerspective( proj, -x, x, -y, y, cam->near, cam->far );
  446.     } else {
  447.     TmOrthographic( proj, -x, x, -y, y, cam->near, cam->far );
  448.     }
  449.     if (cam->flag & CAMF_STEREO)
  450.     TmConcat( cam->stereyes[cam->whicheye], proj, proj );
  451. }
  452.  
  453. /*
  454.  * Computes complete transformation from world -> projected coordinates
  455.  * and leaves it in T.
  456.  * Projected coordinates map the visible world into -1 <= {X,Y,Z} <= 1,
  457.  * with Z = -1 at the near plane and Z = +1 at the far plane.
  458.  */
  459. void
  460. CamView( register Camera *cam, Transform T )
  461. {
  462.     Transform t;
  463.  
  464.     CamViewProjection( cam, t );
  465.     if(cam->flag & CAMF_NEWC2W) {
  466.     TmInvert( cam->camtoworld, cam->worldtocam );
  467.     cam->flag &= ~CAMF_NEWC2W;
  468.     }
  469.     TmConcat( cam->worldtocam, t, T );
  470. }
  471.  
  472. void
  473. CamRotateX( register Camera *cam, float angle )
  474. {
  475.     CtmRotateX( cam->camtoworld, angle );
  476.     cam->flag |= CAMF_NEWC2W;
  477. }
  478.  
  479. void
  480. CamRotateY( register Camera *cam, float angle )
  481. {
  482.     CtmRotateY( cam->camtoworld, angle );
  483.     cam->flag |= CAMF_NEWC2W;
  484. }
  485.  
  486. void
  487. CamRotateZ( register Camera *cam, float angle )
  488. {
  489.     CtmRotateZ( cam->camtoworld, angle );
  490.     cam->flag |= CAMF_NEWC2W;
  491. }
  492.  
  493. /* translate the camera, using the camera's notion of what space it
  494.    is in */
  495. void
  496. CamTranslate( register Camera *cam, float tx, float ty, float tz )
  497. {
  498.   Transform T;
  499.  
  500.   TmSpaceTranslate( T, tx, ty, tz, cam->space );
  501.   TmConcat(T, cam->camtoworld, cam->camtoworld);
  502.   cam->flag |= CAMF_NEWC2W;
  503. }
  504.  
  505. /* CamScale is a noop if the camera is not in Euclidean space */
  506. void
  507. CamScale( register Camera *cam, float sx, float sy, float sz )
  508. {
  509.   if (cam->space == TM_EUCLIDEAN) {
  510.     CtmScale( cam->camtoworld, sx, sy, sz );
  511.     cam->flag |= CAMF_NEWC2W;
  512.   }
  513. }
  514.  
  515. void
  516. CamAlignZ( register Camera *cam, float x, float y, float z )
  517. {
  518.     Point axis;
  519.  
  520.     axis.x = x;
  521.     axis.y = y;
  522.     axis.z = z;
  523.     CtmAlignZ( cam->camtoworld, &axis );
  524.     cam->flag |= CAMF_NEWC2W;
  525. }
  526.  
  527. /*
  528.  * Apply T to camera as seen by world (== T^-1 to world, as seen by camera)
  529.  */
  530. void
  531. CamTransform( register Camera *cam, Transform T )
  532. {
  533.     TmConcat(T, cam->camtoworld, cam->camtoworld);
  534.     cam->flag |= CAMF_NEWC2W;
  535. }
  536.  
  537. static void
  538. CamStereoCompute( register Camera *cam )
  539. {
  540.     float tanconv = tan(cam->stereo_angle);
  541.     TmTranslate( cam->stereyes[CAM_RIGHT], cam->stereo_sep, 0., 0. );
  542.     TmTranslate( cam->stereyes[CAM_LEFT], -cam->stereo_sep, 0., 0. );
  543.     cam->stereyes[CAM_RIGHT][Z][X] = -tanconv;
  544.     cam->stereyes[CAM_LEFT][Z][X] = tanconv;
  545. }
  546.  
  547. /*-----------------------------------------------------------------------
  548.  * Function:    SetHalfField
  549.  * Description:    set camera's "halffield" value
  550.  * Args:    *cam: the camera
  551.  *        halffield: the halffied value to set to
  552.  * Returns:    nothing
  553.  * Author:    mbp
  554.  * Date:    Wed Aug 21 14:26:43 1991
  555.  * Notes:    This procedure modifies the halfyfield member of cam
  556.  *        in such a way as to guarantee that the min half-width
  557.  *        of the view window is halffield.  This depends on the
  558.  *        camera's current aspect ratio.
  559.  */
  560. static void
  561. SetHalfField( register Camera *cam, float halffield )
  562. {
  563.   cam->halfyfield =
  564.     (cam->frameaspect < 1 && cam->frameaspect > 0)
  565.     ? halffield / cam->frameaspect
  566.     : halffield;
  567.   if(cam->flag & CAMF_PERSP)
  568.     cam->halfyfield *= cam->focus;
  569. }
  570.  
  571. /*-----------------------------------------------------------------------
  572.  * Function:    GetHalfField
  573.  * Description:    return camera's "halffield" value
  574.  * Args:    *cam: the camera
  575.  * Returns:    the halffield value
  576.  * Author:    mbp
  577.  * Date:    Wed Aug 21 14:29:31 1991
  578.  * Notes:    the "halffield" is the min half-width of the view
  579.  *        window.  If the aspect ratio is >= 1, this is
  580.  *        the vertical half-width (halfyfield).  If the aspect
  581.  *        ratio is < 1, this is the horizontal half-width.
  582.  */
  583. static float
  584. GetHalfField( register Camera *cam )
  585. {
  586.   float v = cam->halfyfield;
  587.   if(cam->frameaspect < 1) v *= cam->frameaspect;
  588.   if(cam->flag & CAMF_PERSP) v /= cam->focus;
  589.   return v;
  590. }
  591.  
  592. /*
  593.  * Merge one Camera's changed values into another Camera
  594.  */
  595. Camera *
  596. CamMerge(register Camera *src, register Camera *dst)
  597. {
  598.   int chg;
  599.   float fov;
  600.  
  601.   if(src == NULL) return dst;
  602.   if(dst == NULL) return NULL;
  603.  
  604.   chg = src->changed;
  605.  
  606.   if(chg & CAMF_NEWC2W)
  607.     CamSet(dst, CAM_C2WHANDLE, src->c2whandle, CAM_C2W, src->camtoworld, CAM_END);
  608.   if(chg & CAMF_STEREOGEOM)
  609.     CamSet(dst, CAM_STEREOSEP, src->stereo_sep,
  610.             CAM_STEREOANGLE, src->stereo_angle, CAM_END);
  611.   if(chg & CAMF_STEREOXFORM)
  612.     CamSet(dst, CAM_STEREYES, src->stereyes,
  613.             CAM_STERHANDLES, src->sterhandle, CAM_END);
  614.   if(chg & CAMF_W2C)
  615.     CamSet(dst, CAM_W2CHANDLE, src->w2chandle,
  616.             CAM_W2C, src->worldtocam, CAM_END);
  617.   CamGet(src, CAM_FOV, &fov);
  618.   if(chg & CAMF_FOCUS) CamSet(dst, CAM_FOCUS, src->focus, CAM_END);
  619.   if(chg & CAMF_PERSP) CamSet(dst,CAM_PERSPECTIVE,src->flag&CAMF_PERSP,CAM_END);
  620.   if(chg & CAMF_FOV) CamSet(dst, CAM_FOV, fov, CAM_END);
  621.   if(chg & CAMF_ASPECT) CamSet(dst, CAM_ASPECT, src->frameaspect, CAM_END);
  622.   if(chg & CAMF_NEAR) dst->near = src->near;
  623.   if(chg & CAMF_FAR) dst->far = src->far;
  624.   if(chg & CAMF_EYE) dst->whicheye = src->whicheye;
  625.   if(chg & CAMF_STEREO) CamSet(dst,CAM_STEREO,src->flag&CAMF_STEREO,CAM_END);
  626.   if(chg & CAMF_SPACE) dst->space = src->space;
  627.   return dst;
  628. }
  629.